home *** CD-ROM | disk | FTP | other *** search
- VECTORS SEGMENT AT 0H ;Set up segment to intercept Interrupts
- ORG 9H*4 ;The keyboard Interrupt
- KEYBOARD_INT LABEL WORD
- ORG 1CH*4 ;Timer Interrupt
- TIMER_VECTOR LABEL WORD
- VECTORS ENDS
-
- SCREEN SEGMENT AT 0B000H ;A dummy segment to use as the
- SCREEN ENDS ;Extra Segment
-
- ROM_BIOS_DATA SEGMENT AT 40H ;BIOS statuses held here, also keyboard buffer
-
- ORG 1AH
- HEAD DW ? ;Unread chars go from Head to Tail
- TAIL DW ?
- BUFFER DW 16 DUP (?) ;The buffer itself
- BUFFER_END LABEL WORD
-
- ROM_BIOS_DATA ENDS
-
- CODE_SEG SEGMENT
- ASSUME CS:CODE_SEG
- ORG 100H ;ORG = 100H to make this into a .COM file
- FIRST: JMP LOAD_BUFFER ;First time through
-
- COPY_RIGHT DB 'Copyright 1986 Ziff-Davis Publishing Co.'
- BUFF DW 0
- BUFF2 DW 159 DUP(0)
- PAD_OFFSET DW 0 ;Chooses 1st 160 bytes or 2nd
- SCREEN_SEG_OFFSET DW 0 ;0 for mono, 8000H for graphics
- IO_CHAR DW 1 ;Holds addr of Put or Get_Char
- OLD_HEAD DW 1 ;To check for typeahead.
- DISPLAY_ON DB 0 ;0 --> Off.
- STATUS_PORT DW 0 ;Video controller status port
- NEAR_ATTRIB_FLAG DB 0 ;Used in Put_Char
- OLD_KEYBOARD_INT_LABEL LABEL DWORD
- OLD_KEYBOARD_INT DW 0 ;Location of old kbd interrupt
- DW 0
- ROM_TIMER_LABEL LABEL DWORD
- ROM_TIMER DW 0 ;The Timer interrupt's address
- DW 0
-
- BUFSTUFF PROC NEAR ;The keyboard interrupt will now come here.
- ASSUME CS:CODE_SEG
- PUSH AX ;Save the used registers for good form
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH DI
- PUSH SI
- PUSH DS
- PUSH ES
- PUSHF ;First, call old keyboard interrupt
- CALL OLD_KEYBOARD_INT_LABEL
- ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in
- MOV BX,ROM_BIOS_DATA
- MOV DS,BX
- MOV BX,TAIL ;Point to current tail
- CMP BX,HEAD ;If at head, kbd int has deleted char
- JNE CONT
- JMP OUT ;So leave
- CONT: MOV DX,TAIL ;Read a char -- head advances.
- SUB DX,2 ;Point to just read in character
- CMP DX,OFFSET BUFFER ;Did we undershoot buffer?
- JAE NOWRAP ;Nope
- MOV DX,OFFSET BUFFER_END ;Yes -- move to buffer top
- SUB DX,2
- NOWRAP: MOV BX,DX
- MOV CX,[BX] ;Get key in CX
- CMP CX,BUFF ;Is it where we were before?
- JNE T10
- MOV BX,HEAD ;Has the head moved?
- CMP BX,OLD_HEAD
- JE T11 ;If yes, we have moved.
- T10: CMP BUFF2,0
- JNE REMOVE ;If there's something in BUFF2,
- T11: CMP DX,HEAD ; remove char in kbd buffer.
- JNE REMOVE
- JMP OUT ;Do nothing this pass.
- ;More than one char in buffer -- Remove One!
- REMOVE: MOV BX,DX
- MOV TAIL,DX ;Remove character by adjusting tail.
- MOV DX,[BX] ;Store character in buffer.
- MOV CX,80
- MOV BX,0 ;Find end of visitype buffer.
- CHECK: CMP BUFF2[BX],0
- JE BUFEND
- ADD BX,2
- LOOP CHECK
- CMP DX,0E08H ;Was this key a rubout?
- JNE OUT ;No, and buffer filled -- leave.
- MOV BX,158 ;Yes, buff full but rubout last char.
- MOV WORD PTR BUFF[BX],0
- MOV BX,HEAD
- MOV OLD_HEAD,BX ;Store this for next time.
- MOV DX,[BX] ;Always load BUFF.
- MOV BUFF,DX
- JMP OUT ;Can't hold more than 80!
- BUFEND: CMP DX,0E08H ;Rubout (and buffer not full)?
- JNE NODEL ;No, don't del.
- DEL: SUB BX,2 ;Yes, delete last key.
- CMP BX,0FFFEH ;Gone too far?
- JL OUT
- JNE PADDEL
- MOV CX,TAIL ;Del the one char in kdb buffer
- MOV HEAD,CX ; by making tail = head.
- MOV BUFF,0
- JMP SHORT CHKDIS
- PADDEL: MOV DX,0 ;DX --> 0 if we are deleting.
- NODEL: MOV BUFF2[BX],DX ;Load key in Visitype buffer.
- MOV BX,HEAD
- MOV OLD_HEAD,BX ;And store the old head to check later.
- MOV DX,[BX] ;Always reload BUFF.
- MOV BUFF,DX
- CHKDIS: CMP DISPLAY_ON,0 ;Are we on?
- JNE FLASH ;Yes, call DISPLAY
- MOV DISPLAY_ON,0FFH ;Store what's on the screen first.
- MOV PAD_OFFSET,160
- LEA AX,GET_CHAR ;Make IO use Get-Char so it does.
- MOV IO_CHAR,AX
- CALL IO ;Get top line from screen.
- FLASH: CALL DISPLAY ;Display VISITYPE's top line.
- OUT: POP ES ;Having done Pushes, here are the Pops
- POP DS
- POP SI
- POP DI
- POP DX
- POP CX
- POP BX
- POP AX
- IRET ;An interrupt needs an IRET
- BUFSTUFF ENDP
-
- DISPLAY PROC NEAR ;Puts the whole pad on the screen
- PUSH AX
- MOV NEAR_ATTRIB_FLAG,0
- MOV PAD_OFFSET,0 ;Use 1st bytes of pad memory
- LEA AX,PUT_CHAR ;Make IO use Put-Char so it does
- MOV IO_CHAR,AX
- CALL IO ;Put result on screen
- POP AX
- RET ;Leave
- DISPLAY ENDP
-
- GET_CHAR PROC NEAR ;Gets a char from screen and advances position
- ASSUME ES:SCREEN,DS:ROM_BIOS_DATA
- PUSH DX
- MOV SI,2 ;Loop twice, once for char, once for attribute
- MOV DX,STATUS_PORT ;Get ready to read video controller status
- G_WAIT_LOW: ;Start waiting for a new horizontal scan -
- IN AL,DX ;Make sure the video controller scan status
- TEST AL,1 ;is low
- JNZ G_WAIT_LOW
- G_WAIT_HIGH: ;After port has gone low, it must go high
- IN AL,DX ;before it is safe to read directly from
- TEST AL,1 ;the screen buffer in memory
- JZ G_WAIT_HIGH
- MOV AX,ES:[DI] ;Do the move from the screen, one byte at a time
- INC DI ;Move to next screen location
- DEC SI ;Decrement loop counter
- CMP SI,0 ;Are we done?
- JE LEAVE ;Yes
- MOV BUFF[BX],AX ;No -- put char we got into BUFF.
- JMP G_WAIT_LOW ;Do it again
- LEAVE: ADD BX,2
- POP DX
- RET
- GET_CHAR ENDP
-
- PUT_CHAR PROC NEAR ;Puts one char on screen and advances position
- PUSH DX
- CLI
- MOV AH,BYTE PTR BUFF[BX]
- MOV SI,2 ;Loop twice, once for char, once for attribute
- MOV DX,STATUS_PORT ;Get ready to read video controller status
- P_WAIT_LOW: ;Start waiting for a new horizontal scan -
- IN AL,DX ;Make sure the video controller scan status
- TEST AL,1 ;is low
- JNZ P_WAIT_LOW
- P_WAIT_HIGH: ;After port has gone low, it must go high
- IN AL,DX ;before it is safe to write directly to
- TEST AL,1 ;the screen buffer in memory
- JZ P_WAIT_HIGH
- MOV ES:[DI],AH ;Move to screen, one byte at a time
- MOV AH,BYTE PTR BUFF[BX+1]
- CMP NEAR_ATTRIB_FLAG,0
- JNE INCDI
- MOV AH,BYTE PTR BUFF[BX+161]
- INCDI: INC DI ;Point to next screen postion
- DEC SI ;Decrement loop counter
- JNZ P_WAIT_LOW ;If not zero, do it one more time
- ADD BX,2
- POP DX
- STI
- RET ;Exeunt
- PUT_CHAR ENDP
-
- IO PROC NEAR ;This scans over screen positions on top line.
- ASSUME ES:SCREEN ;Use screen as extra segment
- MOV BX,SCREEN
- MOV ES,BX
- MOV DI,SCREEN_SEG_OFFSET ;DI will be pointer to screen postion
- MOV BX,PAD_OFFSET ;BX will be location pointer
- MOV CX,80
- CHAR_LOOP:
- CALL IO_CHAR ;Call Put-Char or Get-Char
- LOOP CHAR_LOOP ;If not zero, scan over next character
- RET ;Finished
- IO ENDP
-
- ASSUME DS:CODE_SEG
- INTERCEPT_TIMER PROC NEAR ;This completes filling the buffer
- ;IF NO KEYS IN BUFFER, PUT NEXT ONE IN.
- PUSH DS ;Save DS since we'll change it
- PUSH CS ;Put current value of CS into DS
- POP DS
- PUSHF
- CALL ROM_TIMER_LABEL ;Make obligatory call
- CMP BUFF2,0
- JNE GO ;No, leave
- JMP OUT1
- GO: CLI ;Yes, start by clearing interrupts
- PUSH ES
- PUSH DS ;Save these.
- PUSH SI
- PUSH DI
- PUSH DX
- PUSH CX
- PUSH BX
- PUSH AX
- ASSUME DS:ROM_BIOS_DATA ;Point to the keyboard buffer again.
- MOV AX,ROM_BIOS_DATA
- MOV DS,AX
- MOV BX,TAIL ;Prepare to put characters in at tail
- CMP HEAD,BX ;If kbd buff not empty, leave.
- JNE FINSTUFF
- STUFF: MOV AX,WORD PTR BUFF2 ;Get the char to put in kbd buffer.
- MOV CX,79 ;Now slide the rest over.
- MOV BX,0
- SLIDE: MOV DX,BUFF[BX+2] ;Do this word by word.
- MOV BUFF[BX],DX
- ADD BX,2
- INC SI
- LOOP SLIDE ;Slides slides BUFF to the left.
- MOV WORD PTR BUFF2[BX-2],0
- MOV DX,HEAD ;Store this to check if user is typing
- MOV OLD_HEAD,DX ; while we drain BUFF.
- MOV DX,TAIL ;Find position in buffer from BX
- ADD DX,2 ;Move to next position for this word
- CMP DX,OFFSET BUFFER_END ;Are we past the end?
- JL NO_WRAP ;No, don't wrap
- MOV DX,OFFSET BUFFER ;Do the Wrap rap.
- NO_WRAP:CMP DX,HEAD ;Buffer full but not yet done?
- JE FINSTUFF ;Time to leave, come back later.
- MOV BX,TAIL ;Prepare to put characters in at tail
- MOV [BX],AX ;Put into buffer
- MOV TAIL,DX ;Reset buffer tail
- FINSTUFF:CMP BUFF2,0
- JNE DIS ;Should we restore the screen?
- MOV BUFF,0
- MOV DISPLAY_ON,0
- MOV PAD_OFFSET,160 ;Use 1st 160 bytes of memory
- LEA AX,PUT_CHAR ;Make IO use Put-Char so it does
- MOV IO_CHAR,AX
- MOV NEAR_ATTRIB_FLAG,0FFH
- CALL IO ;Put result on screen
- JMP SHORT ODIS
- DIS: CALL DISPLAY
- ODIS: POP AX ;Restore these.
- POP BX
- POP CX
- POP DX
- POP DI
- POP SI
- POP DS
- POP ES
- OUT1: POP DS
- IRET ;With customary IRET
- INTERCEPT_TIMER ENDP
-
- LOAD_BUFFER PROC NEAR ;This procedure intializes everything
- ASSUME DS:VECTORS ;The data segment will be the Interrupt area
- MOV AX,VECTORS
- MOV DS,AX
- MOV AX,KEYBOARD_INT ;Get the old interrupt service routine
- MOV OLD_KEYBOARD_INT,AX ;address and put it into our location
- MOV AX,KEYBOARD_INT[2] ;OLD_KEYBOARD_INT so we can call it.
- MOV OLD_KEYBOARD_INT[2],AX
- MOV KEYBOARD_INT,OFFSET BUFSTUFF
- MOV KEYBOARD_INT[2],CS
- MOV AX,TIMER_VECTOR ;Now same for timer
- MOV ROM_TIMER,AX
- MOV AX,TIMER_VECTOR[2]
- MOV ROM_TIMER[2],AX
- MOV TIMER_VECTOR,OFFSET INTERCEPT_TIMER
- MOV TIMER_VECTOR[2],CS ;And intercept that too.
- ASSUME DS:ROM_BIOS_DATA
- MOV AX,ROM_BIOS_DATA
- MOV DS,AX
- MOV BX,OFFSET BUFFER ;Clear the keyboard buffer to start.
- MOV HEAD,BX
- MOV OLD_HEAD,BX
- MOV TAIL,BX
- MOV AH,15 ;Ask for service 15 of INT 10H
- INT 10H ;This tells us how display is set up
- MOV STATUS_PORT,03BAH ;Assume this is a monochrome display
- TEST AL,4 ;Is it?
- JNZ EXIT ;Yes - jump out
- MOV SCREEN_SEG_OFFSET,8000H ;No - set up for graphics display
- MOV STATUS_PORT,03DAH
- EXIT: MOV DX,OFFSET LOAD_BUFFER ;Set up everything but LOAD_BUFFER to
- INT 27H ;stay and attach itself to DOS
- LOAD_BUFFER ENDP
- CODE_SEG ENDS
- END FIRST ;END "FIRST" so 8088 will go to FIRST first.
-